package com.example.kotlindemo.demo

/**
 * Author 87231
 * 2022/8/29 22:36
 *
 * Unit10
 * describe：泛型：将类型参数化，可以用在类、接口、和方法上
 * 声明一个泛型类：
 * class  Box<T>(t:T){ }
 * 一、泛型约束
 * 二、变形：1.声明处型变 2.星号投射
 **/
class Unit10 {
    /**
     * 一、泛型
     */
    class A<T>(t: T) {
        var value = t
    }

    fun testA() {
        var aCls = Unit10.A<String>("name")
        println(aCls.value.javaClass.toString())
        //或者
        var aCls1 = Unit10.A(12)
        println(aCls1.value.javaClass.toString())
    }

    //    定义一个完整的泛型变量,类型参数要放在函数名前面
    fun <T> funB(t: T) = A(t)
    fun testFunB() {
        var funB = funB("123")
        println(funB.value.javaClass.toString())
    }

    //根据传入参数的具体类型来判断参数的具体类型
    fun <T> checkKind(t: T) {
        when (t) {
            is String -> println("value.javaClass is String")
            is Int -> println("value.javaClass is Int")
            is Double -> println("value.javaClass is Double")
            is Boolean -> println("value.javaClass is Boolean")
            else -> println("value.javaClass is other")
        }
    }
    /**
     * 二、变形
     * 1.声明处变形 使用out 和 in
     * 2.类型投影
     */
    /**
     * 声明处变形，使用协变注解修饰符：in 、 out。消费者out，生产者in
     * 使用out使得一个参数的类型发生协变，协变类型参数只能用作输出，可以作为返回值类型但是无法作为入参类型。
     */
    class C<out D>(val d: D) {
        fun testC(): D {
            return d
        }
    }

    class E<in F>(f: F) {
        fun testE(f: F) {
            println(f.toString())
        }
    }

    /**
     * 星号投射
     * 有时候你可能想知道你不知道类的任何参数，但是任然希望能够安全的使用它。这里所谓的安全的使用是指，
     * 对泛型类型定义一个星号投射，要求这个泛型的所有实体实例，都是这个投射的子类型。
     * 对于这个问题，Kotlin提供了一种语法，称为星号投射（start-projection）
     * * 假如类型定义为Foo<out T>，其中T是一个协变的类型参数，上届（upper bound）为TUpper,
     * Foo<>等价于Foo<out TUpper> 它表示，当T未知时，你可以安全的从Foo中读取TUpper类型的值
     * * 假如类型定义为 Foo<in T> , 其中 T 是一个反向协变的类型参数, Foo<> 等价于 Foo<inNothing> .
     * 它表示, 当 T 未知时, 你不能安全地向 Foo<> 写入 任何东西.
     * * 假如类型定义为 Foo<T> , 其中 T 是一个协变的类型参数, 上界(upper bound)为 TUpper ,
     * 对于读取值的场合, Foo<*> 等价于 Foo<out TUpper> , 对于写入值的场合, 等价于 Foo<in Nothing> .
     *
     * 如果一个泛型类型中存在多个类型参数，那么每个类型参数都可以单独的投射。比如，如果类型定义为interface Function<in T,out U>
     *  ,那么可以出现以下几种星号投射
     *  1.Function<*, String> , 代表 Function<in Nothing, String> ;
     *  2. Function<Int, *> , 代表 Function<Int, out Any?> ;
     *  3.Function<, > , 代表 Function<in Nothing, out Any?> .
     *
     */

//    关于星号投射，其实就是*代指了所有类型，相当于Any?
    class G<T>(val t: T, val t2: T, val t3: T)
    class Person(var name: String)


}